package net.zwet.clanworks.connectors.eve;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;

import javax.imageio.ImageIO;
import javax.naming.Context;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import net.zwet.clanworks.connectors.eve.api.utils.DateUtils;
import net.zwet.clanworks.connectors.eve.cache.*;
import net.zwet.clanworks.connectors.eve.api.connector.ConnectionFactory;
import net.zwet.clanworks.connectors.eve.api.connector.EveApiConnector;
import net.zwet.clanworks.connectors.eve.api.exceptions.EveConnectionException;
import net.zwet.clanworks.connectors.eve.api.exceptions.EveAuthenticationException;
import net.zwet.clanworks.connectors.eve.api.images.IEveImageSize;
import net.zwet.clanworks.connectors.eve.api.images.ImageSize;
import net.zwet.clanworks.connectors.eve.api.converter.ServerStatusConverter;
import net.zwet.clanworks.connectors.eve.api.connector.url.Urls;

import net.zwet.clanworks.connectors.eve.jaxb.server.serverstatus.Eveapi;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.util.ByteArrayBuffer;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

public class EveService {
    /**
     * Log variable for all child classes. Uses LogFactory.getLog(getClass()) from Commons Logging
     */
    protected final Log log = LogFactory.getLog(getClass());
    
	private String userID;
	private String vCode;

	private final String PORTRAIT_EXTENSION = ".jpg";
	private final String CORPORATION_LOGO_EXTENSION = ".png";
	private final String ALLIANCE_LOGO_EXTENSION = ".png";

    private Cache cache;
    private Eveapi serverStatus;

    // TODO : Turn EvE API into a SINGLETON.
	
	// --------------------------------------------------------------------------[ constructors ]---
	public EveService() {
		this.userID = "";
		this.vCode = "";

        this.init();
	}
	
	public EveService(Context context, String userId, String vCode) {
		this.userID = userId;
		this.vCode = vCode;

        this.init();
	}


	// --------------------------------------------------------------------------[ Initialize ]-------
	
	private void init() {
//        this.cache = new Cache();

//		try {
//			this.skillTree = this.getSkillTree();
//		} catch (ParseException e) {
//			log.error ("EveService:constructor: " + e.getMessage() );
//		}
	}
	
	// --------------------------------------------------------------------------[ public methods ]---

	/**
	 * 
	 */
	public Eveapi getServerStatus() throws ParseException {
		ServerStatusConverter converter = new ServerStatusConverter();
		
		try {
			if ( this.serverStatus == null ) {
				if ( ! cache.contains(Cache.FILENAME_SERVERSTATUS) ) {
					EveApiConnector connector = ConnectionFactory.getEveApiConnector( this.userID, this.vCode);
					cache.storeFile(connector.getServerStatus(), Cache.FILENAME_SERVERSTATUS);
				}
//				this.parseFile(converter, CacheFileName.SERVER_STATUS);

				this.serverStatus = (Eveapi)converter.getResult( cache.file( Cache.FILENAME_SERVERSTATUS ) ) ;
			}
			
			if ( isValid(serverStatus.getCachedUntil()) ) {
				EveApiConnector connector = ConnectionFactory.getEveApiConnector( this.userID, this.vCode);
				cache.storeFile(connector.getServerStatus(), Cache.FILENAME_SERVERSTATUS);
//				this.parseFile(converter, CacheFileName.SERVER_STATUS);
				this.serverStatus = (Eveapi)converter.getResult(cache.file( Cache.FILENAME_SERVERSTATUS ));
			}
		} catch (IOException e) {
			log.error("EveService:getServerStatus: " + e.getMessage() );
		} catch (EveConnectionException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        return this.serverStatus;
	}
	
	/**
	 * 
	 * @return
	 * @throws java.text.ParseException
	 */
//	public SkillTree getSkillTree() throws ParseException {
//		SkillTreeConverter converter = new SkillTreeConverter();
//
//		try {
//			if ( this.skillTree == null  ) {
//				if ( ! cache.contains(CacheFileName.SKILL_TREE) ) {
//					EveApiConnector connector = ConnectionFactory.getEveApiConnector( this.userID, this.vCode);
//					cache.storeFile(connector.getSkillTree(), CacheFileName.SKILL_TREE);
//				}
//				this.parseFile(converter, CacheFileName.SKILL_TREE );
//				this.skillTree = (SkillTree)converter.getResult();
//			}
//
//			if ( this.skillTree.inValid() ) {
//				EveApiConnector connector = ConnectionFactory.getEveApiConnector( this.userID, this.vCode);
//				cache.storeFile(connector.getSkillTree(), CacheFileName.SKILL_TREE);
//				this.parseFile(converter, CacheFileName.SKILL_TREE );
//				this.skillTree = (SkillTree)converter.getResult();
//			}
//		} catch (EveConnectionException e) {
//			log.error("EveService:getSkillTree: " + e.getMessage() );
//		} catch (IOException e) {
//			log.error("EveService:getSkillTree: " + e.getMessage() );
//		}
//
//		return this.skillTree;
//	}

	/**
	 * 
	 * @return
	 * @throws java.text.ParseException
	 * @throws EveAuthenticationException 
	 */
//	public Characters getCharacters ( String keyID, String vCode ) throws EveAuthenticationException, ParseException {
//		CharactersSelectionConverter converter = new CharactersSelectionConverter();
//		try {
//			if ( this.characters == null ) {
//
//				if ( ! cache.contains(keyID, vCode, CacheFileName.CHARACTERS) ) {
//					EveApiConnector connector = ConnectionFactory.getEveApiConnector( keyID, vCode );
//					cache.storeFile(connector.getCharacters(), CacheFileName.CHARACTERS);
//				}
//				this.parseFile(converter, CacheFileName.CHARACTERS );
//				this.characters = (Characters)converter.getResult();
//			}
//
//			 if ( this.characters.inValid() ) {
//				EveApiConnector connector = ConnectionFactory.getEveApiConnector( keyID, vCode );
//				cache.storeFile(connector.getCharacters(), CacheFileName.CHARACTERS);
//				this.parseFile(converter, CacheFileName.CHARACTERS );
//				this.characters = (Characters)converter.getResult();
//			 }
//		} catch (EveConnectionException e) {
//			log.error("EveService:getCharacters: " + e.getMessage() );
//		} catch (IOException e) {
//			log.error("EveService:getCharacters:n " + e.getMessage() );
//		}
//
//		return this.characters;
//	}

	/**
	 * 
	 * @return
	 * @throws java.text.ParseException
	 * @throws EveAuthenticationException 
	 */
//	public Characters getCharacters() throws ParseException, EveAuthenticationException {
//		if ( ! this.userID.isEmpty() && ! this.vCode.isEmpty() ) {
//			return this.getCharacters(this.userID, this.vCode);
//		} else {
//			throw new EveAuthenticationException();
//		}
//	}


	/**
	 * Retrieve a CharacterSheet from the EvE API for a given character.
	 * 
	 * @param characterID
	 * @return CharacterSheet
	 * @throws java.text.ParseException
	 */
//	public CharacterSheet getCharacterSheet ( String userID, String apiKey, String characterID ) throws ParseException {
//		CharacterSheetConverter converter = new CharacterSheetConverter();
//
//		try {
//			if ( this.characterSheet == null ) {
//				if ( ! cache.contains(CacheFileName.CHARACTER_SHEET) ) {
//					EveApiConnector connector = ConnectionFactory.getEveApiConnector( userID, apiKey );
//					cache.storeFile(connector.getCharacterSheet(characterID), CacheFileName.CHARACTER_SHEET);
//				}
//				this.parseFile(converter, CacheFileName.CHARACTER_SHEET );
//				this.characterSheet = (CharacterSheet)converter.getResult();
//			}
//
//			if ( this.characterSheet.inValid() ) {
//				EveApiConnector connector = ConnectionFactory.getEveApiConnector( userID, apiKey );
//				cache.storeFile(connector.getCharacterSheet(characterID), CacheFileName.CHARACTER_SHEET);
//				this.parseFile(converter, CacheFileName.CHARACTER_SHEET );
//				this.characterSheet = (CharacterSheet)converter.getResult();
//			}
//		} catch (EveConnectionException e) {
//			log.error("EveService:getCharacterSheet: " + e.getMessage() );
//		} catch (IOException e) {
//			log.error("EveService:getCharacterSheet: " + e.getMessage() );
//		}
//
//		return this.characterSheet;
//	}
	
	/**
	 * Retrieve a CharacterSheet from the EvE API for a given character.
	 * 
	 * @param characterID
	 * @return CharacterSheet
	 * @throws java.text.ParseException
	 * @throws EveAuthenticationException 
	 */
//	public CharacterSheet getCharacterSheet ( String characterID ) throws ParseException, EveAuthenticationException {
//		if ( ! this.userID.isEmpty() && ! this.vCode.isEmpty() ) {
//			return ( this.getCharacterSheet(this.userID, this.vCode,  characterID));
//		} else {
//			throw new EveAuthenticationException();
//		}
//
//	}

	/**
	 * Return a SkillQueue form the EvE API for a given Character
	 * 
	 * @param characterID
	 * @return SkillQueue
	 * @throws java.text.ParseException
	 */
//	public SkillQueue getSkillQueue ( String characterID ) throws ParseException {
//		SkillQueueConverter converter = new SkillQueueConverter();
//
//		try {
//			if ( this.skillQueue == null ) {
//
//				if ( ! cache.contains(CacheFileName.SKILL_QUEUE) ) {
//					EveApiConnector connector = ConnectionFactory.getEveApiConnector( this.userID, this.vCode);
//					cache.storeFile(connector.getSkillQueue(characterID), CacheFileName.SKILL_QUEUE);
//				}
//				this.parseFile(converter, CacheFileName.SKILL_QUEUE );
//				this.skillQueue = (SkillQueue)converter.getResult();
//			}
//
//			if ( this.skillQueue.inValid() ) {
//				EveApiConnector connector = ConnectionFactory.getEveApiConnector( this.userID, this.vCode);
//				cache.storeFile(connector.getSkillQueue(characterID), CacheFileName.SKILL_QUEUE);
//				this.parseFile(converter, CacheFileName.SKILL_QUEUE );
//				this.skillQueue = (SkillQueue)converter.getResult();
//			}
//		} catch (EveConnectionException e) {
//			log.error("EveService:getSkillQueue: " + e.getMessage() );
//		} catch (IOException e) {
//			log.error("EveService:getSkillQueue: " + e.getMessage() );
//		}
//
//		return this.skillQueue;
//	}

	// --------------------------------------------------------------------------[ Image methods ]---

	/**
	 * Return an portrait image from the EvE API for a given Character. 
	 * 
	 * The EvE API provides these portraits in the sizes 16x16, 32x32, 64x62, 128x128 and 256x256. 
	 * Because no explicit size has been provided via this method, it will use the default size 64x64.
	 * 
	 * @param characterID
	 * @return Bitmap
	 */
	public BufferedImage getPortrait(int characterID ) {
		return this.getPortrait(characterID, ImageSize.SIZE_64);
	}

	/**
	 * Return an portrait image from the EvE API for a given Character. 
	 * 
	 * The EvE API provides these portraits in the sizes 16x16, 32x32, 64x62, 128x128 and 256x256. 
	 * 
	 * @param characterID
	 * @param size
	 * @return Bitmap
	 */
	public BufferedImage getPortrait(int characterID, IEveImageSize size ) {
		if ( ! cache.fileExists(this.portraitFileName(characterID, size))) {
			this.DownloadPortrait(characterID, size);
		}

		BufferedImage in;
		try {
			in = ImageIO.read( new File( this.portraitFileName(characterID) ) );

			return in;
		} catch (Exception e) {
			log.error("EveService:getPortrait: " + e.getMessage());
			return null;
		}
	}

	/**
	 * Generate a valid filename for locally storing/using a portrait that is retreived from
	 * te EvE API.
	 * 
	 * @param characterID
	 * @return String
	 */
	public String portraitFileName( int characterID ) {
		return this.portraitFileName(characterID, ImageSize.SIZE_64);	
	}

	/**
	 * 
	 * @param characterID
	 * @param size
	 * @return
	 */
	public String portraitFileName( int characterID, IEveImageSize size ) {
		return Integer.toString(characterID) + "_" + Integer.toString(size.getImageSize()) + this.PORTRAIT_EXTENSION;	
	}

	/**
	 * 
	 * @param corporationID
	 * @param size
	 * @return
	 */
	public String corporationLogoFileName( int corporationID, IEveImageSize size ) {
		return Integer.toString(corporationID) + "_" + Integer.toString(size.getImageSize()) + this.CORPORATION_LOGO_EXTENSION;	
	}

	/**
	 * 
	 * @param allianceID
	 * @param size
	 * @return
	 */
	public String allianceLogoFileName( int allianceID, IEveImageSize size ) {
		return Integer.toString(allianceID) + "_" + Integer.toString(size.getImageSize()) + this.ALLIANCE_LOGO_EXTENSION;	
	}

	// --------------------------------------------------------------------------[ utility methods ]---

	/**
	 * parse a given XML storeFile.
	 */
	private void parseFile ( ContentHandler handler, ICacheFile cacheFile ) {
        this.parseFile(handler, "", "", cacheFile);
	}

    private void parseFile ( ContentHandler handler, String keyID, String vCode, ICacheFile cacheFile ) {
        try {
            // Retrieve the xml and handle the content
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();

            XMLReader xr = parser.getXMLReader();

            xr.setContentHandler(handler);

            if ( keyID.isEmpty() && vCode.isEmpty() ) {
                xr.parse(new InputSource( cache.retrieveFile(cache.fileName(keyID, vCode, cacheFile.getFileName() ) ) ) );
            } else {
                xr.parse(new InputSource( cache.retrieveFile( cacheFile.getFileName() ) ) );
            }
            // Check the XML for an error message
        } catch (SAXException saxExc) {
            log.error("SAX Exception: " + saxExc.getMessage());
        } catch (ParserConfigurationException parserExc) {
            log.error("Parser configuration exception: " + parserExc.getMessage());
        } catch (IOException ioe) {
            log.error("IO Exception: " + ioe.getMessage());
        }

    }

    /**
	 * 
	 * @param characterID
	 * @param size
	 */
	private void DownloadPortrait(int characterID, IEveImageSize size) {
		String uri = 
			Urls.CHARACTER_PORTRAIT.getUrl() + 
			Integer.toString(characterID) + 
			"_" + Integer.toString(size.getImageSize()) + 
			this.PORTRAIT_EXTENSION;
		
		this.DownloadImage(uri, this.portraitFileName(characterID, size));
	}

	/**
	 * 
	 * @param corporationID
	 * @param size
	 */
	private void DownloadCorporationLogo(int corporationID, IEveImageSize size) {
		String uri = 
			Urls.CORPORATION_LOGO.getUrl() + 
			Integer.toString(corporationID) + 
			"_" + Integer.toString(size.getImageSize()) + 
			this.CORPORATION_LOGO_EXTENSION;
		
		this.DownloadImage(uri, this.corporationLogoFileName(corporationID, size));
	}

	/**
	 * 
	 * @param allianceID
	 * @param size
	 */
	private void DownloadAllianceLogo(int allianceID, IEveImageSize size) {
		String uri = 
			Urls.ALLIANCE_LOGO.getUrl() + 
			Integer.toString(allianceID) + 
			"_" + Integer.toString(size.getImageSize()) + 
			this.ALLIANCE_LOGO_EXTENSION;
		
		this.DownloadImage(uri, this.corporationLogoFileName(allianceID, size));
	}

	/**
	 * 
	 * @param uri
	 * @param asFilename
	 */
	private void DownloadImage( String uri, String asFilename ) {
		try {
			URL url = new URL(uri);

			/* Open a connection to that URL. */
			URLConnection ucon = url.openConnection();

			/*
			 * Define InputStreams to read from the URLConnection.
			 */
			InputStream is = ucon.getInputStream();
			BufferedInputStream bis = new BufferedInputStream(is);

			/*
			 * Read bytes to the Buffer until there is nothing more to read(-1).
			 */
			ByteArrayBuffer baf = new ByteArrayBuffer(50);
			int current = 0;
			while ((current = bis.read()) != -1) {
				baf.append((byte) current);
			}

			/* Convert the Bytes read to a String. */
			FileOutputStream fos = new FileOutputStream( asFilename);
			fos.write(baf.toByteArray());
			fos.close();
		} catch (IOException e) {
			log.debug("downloadImage: " + e.getMessage());
		}
	}

    public boolean isValid(String cacheExpiration) throws ParseException {
        boolean isValid = true;

        // Version Test ?

        if ( isValid ) {
            Date dateNow = new Date();
            Date cachedUntil = DateUtils.convertToDate(cacheExpiration, DateUtils.EvEDateFormatString);

            Calendar calCacheDate = Calendar.getInstance();
            Calendar calNow = Calendar.getInstance();

            calCacheDate.setTime(cachedUntil);
            calNow.setTime( DateUtils.eveTime( dateNow ) );

            long millisCacheDate = calCacheDate.getTimeInMillis();
            long millisFileDate = calNow.getTimeInMillis();

            isValid = ( millisFileDate <= millisCacheDate );
        }

        return isValid;
    }
	// --------------------------------------------------------------------------[ Getters & Setters ]---

    public Cache getCache() {
        return this.cache;
    }

    public void setCache( Cache cache) {
        this.cache = cache;
    }

	public String getUserID () {
		return this.userID;
	}
	
	public void setUserID ( String userID ) {
		this.userID = userID;
	}
	
	public String getvCode() {
		return this.vCode;
	}
	
	public void setvCode(String vCode) {
		this.vCode = vCode;
	}
}

